package com.strategy.controller;


import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.strategy.entity.ResultResponse;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import com.strategy.core.BaseStrategy;
import com.strategy.core.CompositeStrategy;
import com.strategy.entity.StrategyEntity;
import com.strategy.entity.StrategyExecuteLog;
import com.strategy.manager.StrategyManager;
import com.strategy.mapper.StrategyExecuteLogMapper;
import com.strategy.service.StrategyService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;


/**
 * 策略管理HTTP接口控制器
 */
@Slf4j
@RestController
@RequestMapping("/api/strategies")
@Validated
@Tag(name = "策略管理接口", description = "策略的注册、移除、执行及组合策略管理等功能")
public class StrategyController {

    @Resource
    private StrategyManager strategyManager;

    @Resource
    private StrategyService strategyService;

    @Resource
    private StrategyExecuteLogMapper logMapper;

    private final ObjectMapper objectMapper = new ObjectMapper();

    /**
     * 获取所有策略列表
     */
    @GetMapping
    @Operation(
            summary = "获取所有策略列表",
            description = "返回所有已注册策略的 key 及其版本列表（格式：key: [version1, version2...]）",
            responses = {
                    @ApiResponse(responseCode = "200", description = "查询成功",
                            content = @Content(schema = @Schema(implementation = Map.class))),
                    @ApiResponse(responseCode = "500", description = "服务器内部错误")
            }
    )
    public ResultResponse<Map<String, List<String>>> getAllStrategies() {
        return ResultResponse.success(strategyManager.getAllStrategyKeys());
    }

    /**
     * 分页查询策略
     */
    @GetMapping("/page")
    @Operation(
            summary = "分页查询策略",
            description = "根据策略标识（key）和状态（status）分页查询策略元数据",
            responses = {
                    @ApiResponse(responseCode = "200", description = "查询成功",
                            content = @Content(schema = @Schema(implementation = IPage.class))),
                    @ApiResponse(responseCode = "500", description = "服务器内部错误")
            }
    )
    public ResultResponse<IPage<StrategyEntity>> getStrategyPage(
            @RequestParam(defaultValue = "1")
            @Parameter(name = "pageNum", description = "当前页码（默认1）", example = "1")
            Integer pageNum,

            @RequestParam(defaultValue = "10")
            @Parameter(name = "pageSize", description = "每页数量（默认10）", example = "10")
            Integer pageSize,

            @RequestParam(required = false)
            @Parameter(name = "key", description = "策略标识（可选过滤条件）", example = "calculator:add")
            String key,

            @RequestParam(required = false)
            @Parameter(name = "status", description = "策略状态（可选过滤条件，如1-启用/0-禁用）", example = "1")
            Integer status) {
        return ResultResponse.success(strategyService.queryPage(new Page<>(pageNum, pageSize), key, status));
    }

    /**
     * 注册新策略（添加校验和日志）
     */
    @PostMapping("/register")
    @Operation(
            summary = "注册新策略",
            description = "通过策略类名（className）反射实例化策略并注册到系统中",
            responses = {
                    @ApiResponse(responseCode = "200", description = "注册成功",
                            content = @Content(schema = @Schema(implementation = Boolean.class))),
                    @ApiResponse(responseCode = "400", description = "参数校验失败（如className为空）"),
                    @ApiResponse(responseCode = "500", description = "策略类不存在或实例化失败")
            }
    )
    public ResultResponse<Boolean> registerStrategy(@RequestBody
                                    @Parameter(name = "strategyEntity", description = "策略元数据（需包含className）",
                                            schema = @Schema(implementation = StrategyEntity.class))
                                    StrategyEntity strategyEntity) {
        try {
            // 校验 className 非空
            String className = strategyEntity.getClassName();
            if (className == null || className.isEmpty()) {
                throw new IllegalArgumentException("策略类名（className）不能为空");
            }

            // 反射获取无参构造器并创建实例（替代 newInstance()）
            Class<?> strategyClass = Class.forName(className);
            // 获取无参构造器（若目标类无无参构造器，会抛出 NoSuchMethodException）
            var constructor = strategyClass.getDeclaredConstructor();
            constructor.setAccessible(true); // 允许访问私有构造器
            BaseStrategy<?, ?, ?> strategy = (BaseStrategy<?, ?, ?>) constructor.newInstance();

            strategyManager.registerStrategy(strategy, true);
            return ResultResponse.success(true);
        } catch (ClassNotFoundException e) {
            log.error("策略类未找到，className={}", strategyEntity.getClassName(), e);
            throw new RuntimeException("策略类未找到", e);
        } catch (NoSuchMethodException e) {
            log.error("策略类无无参构造器，className={}", strategyEntity.getClassName(), e);
            throw new RuntimeException("策略类无无参构造器", e);
        } catch (InvocationTargetException e) {
            log.error("策略构造器执行异常，className={}", strategyEntity.getClassName(), e.getCause());
            throw new RuntimeException("策略构造器执行异常", e.getCause());
        } catch (IllegalAccessException | InstantiationException e) {
            log.error("策略实例化失败，className={}", strategyEntity.getClassName(), e);
            throw new RuntimeException("策略实例化失败", e);
        } catch (Exception e) {
            log.error("注册策略失败，strategyEntity={}", strategyEntity, e);
            throw new RuntimeException("注册策略失败", e);
        }
    }

    /**
     * 移除策略
     */
    @DeleteMapping("/{key}/{version}")
    @Operation(
            summary = "移除策略",
            description = "根据策略标识（key）和版本（version）移除已注册的策略",
            responses = {
                    @ApiResponse(responseCode = "200", description = "移除成功",
                            content = @Content(schema = @Schema(implementation = Boolean.class))),
                    @ApiResponse(responseCode = "400", description = "参数缺失（key/version）"),
                    @ApiResponse(responseCode = "404", description = "策略不存在")
            }
    )
    public ResultResponse<Boolean> unRegisterStrategy(
            @RequestParam
            @Parameter(name = "key", description = "策略标识", required = true, example = "calculator:add")
            String key,

            @RequestParam
            @Parameter(name = "version", description = "策略版本", required = true, example = "1.0.0")
            String version) {
        strategyManager.unregisterStrategy(key, version, true);
        return ResultResponse.success(true);
    }

    /**
     * 设置默认版本
     */
    @PutMapping("/default")
    @Operation(
            summary = "设置策略默认版本",
            description = "根据策略ID（id）将指定版本设为该策略的默认版本",
            responses = {
                    @ApiResponse(responseCode = "200", description = "设置成功",
                            content = @Content(schema = @Schema(implementation = Boolean.class))),
                    @ApiResponse(responseCode = "400", description = "参数缺失（id）"),
                    @ApiResponse(responseCode = "404", description = "策略不存在")
            }
    )
    public ResultResponse<Boolean> setDefaultVersion(@RequestParam Long id) {
        return ResultResponse.success(strategyService.setAsDefault(id));
    }

    /**
     * 执行策略
     * 参数格式
     * {
     * "params": {
     * "value":1
     * },
     * "input":1
     * }
     */
    @PostMapping("/execute/{key}")
    @Operation(
            summary = "执行策略",
            description = "根据策略标识（key）和版本（version）执行策略，参数格式：{\"params\":{\"value\":1},\"input\":1}",
            responses = {
                    @ApiResponse(responseCode = "200", description = "执行成功",
                            content = @Content(schema = @Schema(implementation = Object.class))),
                    @ApiResponse(responseCode = "400", description = "参数缺失（key/version/input/params）"),
                    @ApiResponse(responseCode = "404", description = "策略不存在")
            }
    )
    public ResultResponse<Object> executeStrategy(
            @PathVariable
            @Parameter(name = "key", description = "策略标识", required = true, example = "calculator:add")
            String key,

            @RequestParam(required = false)
            @Parameter(name = "version", description = "策略版本", example = "1.0.0")
            String version,

            @RequestBody
            @Parameter(name = "request", description = "执行参数（包含input和params）", required = true, schema = @Schema(implementation = Map.class))
            Map<String, Object> request) {
        Object input = request.get("input");
        Map<String, Object> params = objectMapper.convertValue(request.get("params"), new TypeReference<>() {
        });
        return ResultResponse.success(strategyManager.executeStrategy(key, version, input, params));
    }

    /**
     * 创建组合策略（修复类型转换问题）
     */
    @PostMapping("/composite")
    @Operation(
            summary = "创建组合策略",
            description = "将多个前置策略与最终策略组合成新策略，支持链式执行",
            responses = {
                    @ApiResponse(responseCode = "200", description = "创建成功",
                            content = @Content(schema = @Schema(implementation = Boolean.class))),
                    @ApiResponse(responseCode = "400", description = "参数格式错误（如策略key不存在）"),
                    @ApiResponse(responseCode = "500", description = "策略实例化或类型转换失败")
            }
    )
    public <O> ResultResponse<Boolean> createCompositeStrategy(
            @RequestParam
            @Parameter(name = "key", description = "组合策略标识", required = true, example = "calculator:add-multiply")
            String key,

            @RequestParam
            @Parameter(name = "version", description = "组合策略版本", required = true, example = "1.0.0")
            String version,

            @RequestParam
            @Parameter(name = "description", description = "组合策略描述", required = true, example = "先加后乘组合策略")
            String description,

            @RequestBody
            @Parameter(name = "strategyKeys", description = "前置策略的key:version列表（如[\"calculator:add:1.0.0\"]）",
                    required = true,
                    // 修复：使用 implementation 指定数组元素类型为 String
                    schema = @Schema(type = "array", implementation = String.class))
            List<String> strategyKeys,

            @RequestParam
            @Parameter(name = "finalStrategyKey", description = "最终策略的key:version（如\"calculator:multiply:1.0.0\"）",
                    required = true, example = "calculator:multiply:1.0.0")
            String finalStrategyKey) {
        try {
            // 解析前置策略（中间类型为 O，输出类型无约束）
            List<BaseStrategy<?, O, ?>> preStrategies = new ArrayList<>();
            for (String strategyKey : strategyKeys) {
                String[] parts = strategyKey.split(":");
                BaseStrategy<?, ?, ?> rawStrategy = strategyManager.getStrategy(parts[0], parts.length > 1 ? parts[1] : null);
                if (rawStrategy == null) {
                    throw new IllegalArgumentException("前置策略不存在，key: " + strategyKey);
                }
                // 显式声明转换安全（假设 strategyManager 已保证类型兼容）
                @SuppressWarnings("unchecked")
                BaseStrategy<?, O, ?> typedStrategy = (BaseStrategy<?, O, ?>) rawStrategy;
                preStrategies.add(typedStrategy);
            }

            // 解析最终策略（输入类型为 O，输出类型无约束）
            String[] finalParts = finalStrategyKey.split(":");
            BaseStrategy<?, O, ?> finalStrategy = strategyManager.getStrategy(
                    finalParts[0], finalParts.length > 1 ? finalParts[1] : null);
            if (finalStrategy == null) {
                throw new IllegalArgumentException("最终策略不存在，key: " + finalStrategyKey);
            }

            // 显式指定 CompositeStrategy 泛型（输入I=?, 中间O=O, 输出R=?）
            CompositeStrategy<?, O, ?> composite = new CompositeStrategy<>(
                    key, version, description, preStrategies, finalStrategy);

            strategyManager.registerCompositeStrategy(composite, true);
            return ResultResponse.success(true);
        } catch (Exception e) {
            log.error("创建组合策略失败，key={}, version={}", key, version, e);
            throw new RuntimeException("创建组合策略失败", e);
        }
    }

    /**
     * 查询策略执行日志
     */
    @GetMapping("/logs")
    @Operation(
            summary = "查询策略执行日志",
            description = "根据策略标识（key）和执行状态（status）分页查询策略执行记录",
            responses = {
                    @ApiResponse(responseCode = "200", description = "查询成功",
                            content = @Content(schema = @Schema(implementation = IPage.class))),
                    @ApiResponse(responseCode = "500", description = "服务器内部错误")
            }
    )
    public ResultResponse<IPage<StrategyExecuteLog>> getExecuteLogs(
            @RequestParam(defaultValue = "1")
            @Parameter(name = "pageNum", description = "当前页码（默认1）", example = "1")
            Integer pageNum,

            @RequestParam(defaultValue = "10")
            @Parameter(name = "pageSize", description = "每页数量（默认10）", example = "10")
            Integer pageSize,

            @RequestParam(required = false)
            @Parameter(name = "key", description = "策略标识（可选过滤条件）", example = "calculator:add")
            String key,

            @RequestParam(required = false)
            @Parameter(name = "status", description = "执行状态（可选过滤条件，1-成功/0-失败）", example = "1")
            Integer status) {
        LambdaQueryWrapper<StrategyExecuteLog> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(StrategyExecuteLog::getStrategyKey, key);
        wrapper.eq(status != null, StrategyExecuteLog::getStatus, status);
        wrapper.orderByDesc(StrategyExecuteLog::getId);
        return ResultResponse.success(logMapper.selectPage(new Page<>(pageNum, pageSize), wrapper));
    }
}